#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"

namespace ThreadPoolModule
{
    using namespace LogMudule;
    using namespace ThreadModule;
    using namespace LockModule;
    using namespace CondModule;

    using thread_t = std::shared_ptr<Thread>;

    template <typename T>
    class ThreadPool
    {
    private:
        const static int defaultnum = 5;
        bool IsEmpty()
        {
            return _taskq.empty();
        }
        void HandlerTask(std::string name)
        {
            LOG(LogLevel::DEBUG) << name << "进入主逻辑";

            // 拿任务
            while (true)
            {
                T t;
                {
                    LockGuard l(_lock);

                    while (IsEmpty() && _isrunning)
                    {
                        LOG(LogLevel::DEBUG) << name << "进入等待";
                        _wait_num++;
                        _cond.Wait(_lock);
                        _wait_num--;
                        LOG(LogLevel::DEBUG) << name << "完成等待";
                    }

                    if (IsEmpty() && !_isrunning)
                    {
                        break;
                    }

                    t = _taskq.front();
                    _taskq.pop();
                }

                LOG(LogLevel::DEBUG) << "开始执行任务了";
                // 执行任务
                t();
            }
        }

        ThreadPool(int num = defaultnum) : _num(num), _wait_num(0), _isrunning(false)
        {
            for (int i = 0; i < _num; i++)
            {
                _threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));
                LOG(LogLevel::DEBUG) << "对象" << i << "完成创建";
            }
        }

        ThreadPool(const ThreadPool<T> &tp) = delete;
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;

    public:
        static ThreadPool<T> *getInstance()
        {
            if (tp == nullptr)
            {
                LockGuard l(_mutex);
                if (tp == nullptr)
                {
                    tp = new ThreadPool<T>();
                    LOG(LogLevel::DEBUG) << "第一次完成创建";
                }
                tp->Start();
            }

            LOG(LogLevel::DEBUG) << "返回线程池";
            return tp;
        }

        void Equeue(const T &in)
        {
            if (_isrunning == false)
            {
                LOG(LogLevel::DEBUG) << "线程池关闭";
                return;
            }

            LOG(LogLevel::DEBUG) << "加入任务";
            LockGuard l(_lock);
            _taskq.push(in);
            if (_wait_num > 0)
            {
                _cond.Notify();
                LOG(LogLevel::DEBUG) << "唤醒一个线程";
            }
        }
        void Start()
        {
            if (_isrunning)
                return;

            _isrunning = true;
            for (auto &e : _threads)
            {
                e->Start();
            }
        }
        void Wait()
        {
            for (auto &e : _threads)
            {
                e->Join();
            }
        }
        void Stop()
        {
            LockGuard l(_lock);
            if (_isrunning == false)
                return;

            _isrunning = false;
            if (_wait_num > 0)
            {
                _cond.NotifyAll();
            }
        }
        ~ThreadPool()
        {
        }

    private:
        std::vector<thread_t> _threads; // 线程组s
        int _num;                       // 线程数量
        int _wait_num;                  // 正在等待的线程数量
        std::queue<T> _taskq;           // 临界资源
        Mutex _lock;                    // 锁
        Cond _cond;                     // 条件变量
        bool _isrunning;                // 当前线程状态

        static ThreadPool<T> *tp; // 线程池指针
        static Mutex _mutex;      // 为单例的锁
    };

    template <typename T>
    ThreadPool<T> *ThreadPool<T>::tp = nullptr;

    template <typename T>
    Mutex ThreadPool<T>::_mutex;
}
